﻿package com.godpaper.controllers.mxml.ds
{
	import de.polygonal.ds.DLinkedList;
	import de.polygonal.ds.DListIterator;
	import de.polygonal.ds.DListNode;
	
	import flash.display.*;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.core.UIComponent;
	
	public class SinglyLinkedList extends UIComponent
	{
		public static var SW:Number         = 550;
		public static var SH:Number         = 100;
		public static var BLUE:uint         = 0X03A3F0;
		public static var PURPLE:uint       = 0XF40095;
		[Bindable] public var START_NODES:Number;
		[Bindable] public var RADIUS_OFFSET:int = 5;
		
		public var dll:DLinkedList;
		
		public var canvas:UIComponent;
		
		public function SinglyLinkedList(startNodes:Number,maxNodeRadius:Number=15)
		{
			START_NODES = startNodes;
			Node.NODE_RADIUS = maxNodeRadius;
			
			addEventListener(Event.ADDED_TO_STAGE,function(event:Event):void
			{
				stage.scaleMode = StageScaleMode.NO_SCALE;
				stage.align = StageAlign.TOP_LEFT;
			});
			
			dll = new DLinkedList();
			
			for (var i:int = 0; i < START_NODES; i++)
			{
				var node:Node = new Node();
				node.removeEventListener(MouseEvent.MOUSE_DOWN, node.mouseHandler);
				node.removeEventListener(MouseEvent.MOUSE_UP,   node.mouseHandler);
				
				addChild(node);
				
				dll.append(node);
			}
			
			addChild(canvas = new UIComponent());
			
			align();
			drawLinks();
		}
		
		public function align():void
		{
			var xEdgeOffset:Number =  Node.NODE_RADIUS + 10;
			var xSpacing:Number = (SW - (2 * xEdgeOffset)) / (Node.NUM_NODES - 1);
			
			var r:Number = Math.min(Node.NODE_RADIUS * (START_NODES) / Node.NUM_NODES, Node.NODE_RADIUS);
			
			var i:int = 0;
			for (var itr:DListIterator = dll.getListIterator(); itr.valid(); itr.forth())
			{
				var node:Node = itr.data as Node;
				node.setPos(xEdgeOffset + (i++ * xSpacing), SH / 2);
				
				node.radius = r;
				node.draw();
			}
		}
		
		public function drawLinks():void
		{
			var g:Graphics = canvas.graphics;
			
			g.clear();
			g.lineStyle(0);
			
			var dlnode:DListNode;
			
			var offsetAngle:Number = 0;
			
			for (var itr:DListIterator = dll.getListIterator(); itr.valid(); itr.forth())
			{
				dlnode = itr.node;
				
				var currNode:Node = dlnode.data;
				
				if (!dlnode.next) break;
				
				var nextNode:Node = dlnode.next.data;
				
				var dir0:Point = new Point(1, 0);
				var dir1:Point = new Point(1, 0);
				
				var r:Number = currNode.radius + RADIUS_OFFSET;
				
				//curr----->next
				var anchorCurr:Point = new Point(currNode.x + r, currNode.y);
				
				//curr<----next
				var anchorNext:Point = new Point(nextNode.x - r, nextNode.y);
				
				drawLink(g, anchorCurr, anchorNext, PURPLE, 1);
			}
		}
		
		public function drawLink(g:Graphics, a:Point, b:Point, clr:uint, offset:Number = 0):void
		{
			var dir:Point = new Point(b.x - a.x, b.y - a.y);
			dir.normalize(1);
			var dist:Number = Point.distance(a, b) - 2 * offset;
			g.lineStyle(2, clr, 1);
			
			drawArrow(g, a.x + dir.x * offset, a.y + dir.y * offset, dir.x, dir.y, dist, 4);
		}
		
		public function drawArrow(g:Graphics, x:Number, y:Number, xdir:Number, ydir:Number, len:Number, size:Number):void
		{
			var t:Number = 1 / Math.sqrt(xdir * xdir + ydir * ydir);
			
			var ex:Number = (xdir * t * len) + x;
			var ey:Number = (ydir * t * len) + y;
			
			g.moveTo(x, y);
			g.lineTo(ex, ey);
			
			var dx:Number = (ex - x);
			var dy:Number = (ey - y);
			
			var l:Number = Math.sqrt((dx * dx) + (dy * dy));
			if (l <= 0)	return;
			
			dx /= l;
			dy /= l;
			var nx:Number = -dy;
			var ny:Number =  dx;
			g.moveTo(ex, ey);
			g.lineTo((ex - (size * dx)) - (size * nx), (ey - (size * dy)) - (size * ny));
			g.moveTo(ex, ey);
			g.lineTo((ex - (size * dx)) + (size * nx), (ey - (size * dy)) + (size * ny));
		}
	}
}